package com.atguigu.gulimall.order.service.impl;

import com.alibaba.fastjson.JSON;
import com.alipay.api.domain.PayInfoDTO;
import com.atguigu.common.constant.OrderConstant;
import com.atguigu.common.exception.BizErrorCode;
import com.atguigu.common.to.mq.SeckillOrderTo;
import com.atguigu.common.to.order.OrderTo;
import com.atguigu.common.to.product.SpuInfoTo;
import com.atguigu.common.to.ware.FreightRespTo;
import com.atguigu.common.to.ware.HasSkuStockTo;
import com.atguigu.common.to.ware.MemberAddressTo;
import com.atguigu.common.to.ware.WareSkuLockRequestTo;
import com.atguigu.common.utils.PageUtils;
import com.atguigu.common.utils.Query;
import com.atguigu.common.utils.R;
import com.atguigu.common.vo.MemberResVo;
import com.atguigu.gulimall.order.config.OrderRabbitMQConfig;
import com.atguigu.gulimall.order.dao.OrderDao;
import com.atguigu.gulimall.order.entity.OrderEntity;
import com.atguigu.gulimall.order.entity.OrderItemEntity;
import com.atguigu.gulimall.order.entity.PaymentInfoEntity;
import com.atguigu.gulimall.order.feign.CartFeignService;
import com.atguigu.gulimall.order.feign.MemberFeignService;
import com.atguigu.gulimall.order.feign.ProductFeignService;
import com.atguigu.gulimall.order.feign.WareFeignService;
import com.atguigu.gulimall.order.interceptor.LoginInterceptor;
import com.atguigu.gulimall.order.service.OrderItemService;
import com.atguigu.gulimall.order.service.OrderService;
import com.atguigu.gulimall.order.service.PaymentInfoService;
import com.atguigu.gulimall.order.to.OrderCreateTo;
import com.atguigu.gulimall.order.to.OrderReleaseTo;
import com.atguigu.gulimall.order.vo.*;
import com.baomidou.mybatisplus.core.conditions.query.QueryWrapper;
import com.baomidou.mybatisplus.core.metadata.IPage;
import com.baomidou.mybatisplus.core.toolkit.IdWorker;
import com.baomidou.mybatisplus.extension.service.impl.ServiceImpl;
import com.fasterxml.jackson.core.type.TypeReference;
import lombok.extern.slf4j.Slf4j;
import org.springframework.amqp.rabbit.core.RabbitTemplate;
import org.springframework.beans.BeanUtils;
import org.springframework.beans.factory.annotation.Autowired;
import org.springframework.beans.factory.annotation.Qualifier;
import org.springframework.data.redis.core.StringRedisTemplate;
import org.springframework.data.redis.core.script.DefaultRedisScript;
import org.springframework.scheduling.concurrent.ThreadPoolTaskExecutor;
import org.springframework.stereotype.Service;
import org.springframework.transaction.annotation.Transactional;
import org.springframework.web.context.request.RequestAttributes;
import org.springframework.web.context.request.RequestContextHolder;

import java.math.BigDecimal;
import java.math.RoundingMode;
import java.util.*;
import java.util.concurrent.CompletableFuture;
import java.util.stream.Collectors;


@Slf4j
@Service("orderService")
public class OrderServiceImpl extends ServiceImpl<OrderDao, OrderEntity> implements OrderService {


    @Autowired
    private MemberFeignService memberFeignService;

    @Autowired
    private CartFeignService cartFeignService;

    @Qualifier("taskExecutor")
    @Autowired
    private ThreadPoolTaskExecutor taskExecutor;

    @Autowired
    private WareFeignService wareFeignService;

    @Autowired
    private StringRedisTemplate stringRedisTemplate;

    @Autowired
    private ProductFeignService productFeignService;

    @Autowired
    private OrderItemService orderItemService;

    @Autowired
    private RabbitTemplate rabbitTemplate;

    @Autowired
    private PaymentInfoService paymentInfoService;

    @Override
    public PageUtils queryPage(Map<String, Object> params) {
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
        );

        return new PageUtils(page);
    }

    @Override
    public OrderConfirmVo confirmOrder() {
        OrderConfirmVo vo = new OrderConfirmVo();
        MemberResVo info = LoginInterceptor.getLoginUserInfo();

        // 获取线程请求
        RequestAttributes attributes = RequestContextHolder.getRequestAttributes();

        // 获取用户地址信息
        CompletableFuture<Void> addressTask = CompletableFuture.runAsync(() -> {
            // 为每个任务设置线程上下文信息
            RequestContextHolder.setRequestAttributes(attributes);

            List<OrderConfirmVo.MemberAddressVo> memberAddressVos = memberFeignService.listAddressByMemberId(info.getId());
            vo.setAddresses(memberAddressVos);
        }, taskExecutor);

        // 获取 订单项信息（购物车已选中商品的信息）
        CompletableFuture<Void> itemTask = CompletableFuture.supplyAsync(() -> {
            // 为每个任务设置线程上下文信息
            RequestContextHolder.setRequestAttributes(attributes);

            List<OrderConfirmVo.OrderItemVo> orderItemVos = cartFeignService.cartItemsByMemberId(info.getId());
            vo.setItems(orderItemVos);
            return orderItemVos;
        }, taskExecutor).thenAccept(orderItemVos -> {
            List<Long> skuIds = orderItemVos.stream().map(OrderConfirmVo.OrderItemVo::getSkuId).collect(Collectors.toList());
            R r = wareFeignService.hasSkuStock(skuIds);
            if (r.isOk()) {
                List<HasSkuStockTo> data = r.getData(new TypeReference<List<HasSkuStockTo>>() {
                });
                if (data != null) {
                    Map<Long, Boolean> stocks = data.stream().collect(Collectors.toMap(HasSkuStockTo::getSkuId, HasSkuStockTo::getHasStock));
                    vo.setStocks(stocks);
                }
            }
        });


        // 设置积分
        vo.setIntegration(info.getIntegration());

        // 总额与减免金额自动计算

        // todo 防重令牌
        String token = UUID.randomUUID().toString();
        vo.setOrderToken(token);

        // redis 保存token信息
        String tokenKey = OrderConstant.ORDER_SUBMIT_TOKEN_PREFIX + info.getId();
        stringRedisTemplate.opsForValue().set(tokenKey, token, OrderConstant.ORDER_SUBMIT_TOKEN_TIMEOUT);
        CompletableFuture.allOf(addressTask, itemTask).join();
        return vo;
    }

    //    @GlobalTransactional
    @Transactional
    @Override
    public OrderSubmitResponseVo orderSubmit(OrderSubmitRequestVo vo) {


        OrderSubmitResponseVo responseVo = new OrderSubmitResponseVo();
        // 获取用户登录信息
        MemberResVo userInfo = LoginInterceptor.getLoginUserInfo();
        String tokenKey = OrderConstant.ORDER_SUBMIT_TOKEN_PREFIX + userInfo.getId();
        String clientToken = vo.getOrderToken();
        Long result = stringRedisTemplate.execute(new DefaultRedisScript<>(OrderConstant.REDIS_LUA_SCRIPT, Long.class)
                , Collections.singletonList(tokenKey), clientToken);
        if (result != null && result.equals(0L)) {
            // 客户端token 与服务端token 不同或者过期
            responseVo.setCode(1);
            responseVo.setErrorCode(BizErrorCode.ORDER_TOKEN_VALID_ERROR);
            return responseVo;
        }
        // 生成订单信息
        OrderCreateTo to = buildOrderInfo(vo, userInfo);

        // 比价
        if (Math.abs(to.getOrder().getPayAmount().subtract(vo.getPayPrice()).doubleValue()) > 0.01) {
            // 价格不正常
            responseVo.setCode(2);
            log.error("比价结果超出预期 0.01 ，页面提交价格为 {}, 实际价格为 {}", vo.getPayPrice(), to.getOrder().getPayAmount());
            responseVo.setErrorCode(BizErrorCode.ORDER_PARITY_ERROR);
            return responseVo;
        }

        // 保存订单
        this.saveOrderInfo(to);
        // 锁定库存
        WareSkuLockRequestTo wareSkuLockRequestTo = new WareSkuLockRequestTo();
        wareSkuLockRequestTo.setOrderSn(to.getOrder().getOrderSn());
        List<WareSkuLockRequestTo.WareSkuLockItem> lockItems = to.getItems().stream().map(oi -> {
            WareSkuLockRequestTo.WareSkuLockItem wareSkuLockItem = new WareSkuLockRequestTo.WareSkuLockItem();
            wareSkuLockItem.setSkuId(oi.getSkuId());
            wareSkuLockItem.setSkuName(oi.getSkuName());
            wareSkuLockItem.setCount(oi.getSkuQuantity());
            return wareSkuLockItem;
        }).collect(Collectors.toList());
        wareSkuLockRequestTo.setLockItems(lockItems);
        R r = wareFeignService.lockSkuItem(wareSkuLockRequestTo);
        if (!r.isOk()) {
            // 库存锁定失败
            responseVo.setCode(3);
            log.error("库存锁定失败 ，订单号为 {}, 订单项为 {}", wareSkuLockRequestTo.getOrderSn(), wareSkuLockRequestTo.getLockItems());
            responseVo.setErrorCode(BizErrorCode.NO_ENOUGH_STOCK_ERROR);
            return responseVo;
        }


        responseVo.setOrder(to.getOrder());

        // todo 远程扣件积分  订单回滚库存不回滚
//        int i = 10/0;
        // 发送订单消息到消息队列中
        OrderReleaseTo orderReleaseTo = new OrderReleaseTo();
        orderReleaseTo.setOrderSn(to.getOrder().getOrderSn());
        rabbitTemplate.convertAndSend(OrderRabbitMQConfig.ORDER_EVENT_EXCHANGE_NAME, OrderRabbitMQConfig.ORDER_CREATE_ROUTE_KEY_NAME, orderReleaseTo);


        return responseVo;
    }

    @Override
    public OrderEntity getOrderInfoByOrderSn(String orderSn) {
        return this.baseMapper.selectOne(new QueryWrapper<OrderEntity>().eq("order_sn", orderSn));
    }

    @Override
    public void cancelOrder(String orderSn) {
        // 获取订单信息
        OrderEntity order = this.getOrderInfoByOrderSn(orderSn);
        log.info("收到订单过期消息， 订单号为 {}, 订单信息为 {}", orderSn, order);
        // 判断订单状态是否为新建
        if (order.getStatus().equals(OrderConstant.OrderStatusEnum.CREATE_NEW.getCode())) {
            log.info("订单状态为 新建状态，可以进行解锁，订单号为 {}", orderSn);
            OrderEntity newOrder = new OrderEntity();
            newOrder.setId(order.getId());
            newOrder.setStatus(OrderConstant.OrderStatusEnum.CANCELED.getCode());
            this.updateById(newOrder);

            log.info("发送解锁库存消息，订单号为 {}", orderSn);
            OrderTo orderTo = new OrderTo();
            BeanUtils.copyProperties(order, orderTo);
            orderTo.setStatus(OrderConstant.OrderStatusEnum.CANCELED.getCode());
            rabbitTemplate.convertAndSend(OrderRabbitMQConfig.ORDER_EVENT_EXCHANGE_NAME, OrderRabbitMQConfig.ORDER_RELEASE_OTHER_ROUTE_KEY_NAME, orderTo);
        }
    }

    @Override
    public PayVo getOrderPayVo(String orderSn) {
        OrderEntity order = this.getOrderInfoByOrderSn(orderSn);
        PayVo payVo = new PayVo();
        payVo.setOut_trade_no(orderSn);
//        List<OrderItemEntity> orderItems = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
//        payVo.setSubject(orderItems.get(0).getSkuName());
        payVo.setSubject("test order");
        payVo.setTotal_amount(order.getPayAmount().setScale(2, RoundingMode.HALF_UP).toString());
//        payVo.setBody(orderItems.get(0).getSkuAttrsVals());

        return payVo;
    }

    @Override
    public PageUtils queryPageWithItem(Map<String, Object> params) {
        MemberResVo userInfo = LoginInterceptor.getLoginUserInfo();
        Long userId = userInfo.getId();
        IPage<OrderEntity> page = this.page(
                new Query<OrderEntity>().getPage(params),
                new QueryWrapper<OrderEntity>()
                        .eq("member_id", userId)
                        .orderByDesc("id")
        );

        for (OrderEntity record : page.getRecords()) {
            String orderSn = record.getOrderSn();
            List<OrderItemEntity> items = orderItemService.list(new QueryWrapper<OrderItemEntity>().eq("order_sn", orderSn));
            record.setItems(items);
        }

        return new PageUtils(page);
    }

    @Override
    public String handleAliPayed(PayAsyncVo vo) {
        PaymentInfoEntity info = new PaymentInfoEntity();
        info.setOrderSn(vo.getOut_trade_no());
        info.setAlipayTradeNo(vo.getTrade_no());
        info.setTotalAmount(new BigDecimal(vo.getTotal_amount()));
        info.setSubject(vo.getSubject());
        info.setPaymentStatus(vo.getTrade_status());
        info.setCreateTime(vo.getGmt_create());
        info.setConfirmTime(vo.getGmt_payment());
        info.setCallbackTime(vo.getNotify_time());
        paymentInfoService.save(info);

        // 修改订单状态
        if (OrderConstant.AliPayTradeStatusEnum.valueOf(vo.getTrade_status()).isOk()) {
            this.baseMapper.updateOrderStatus(info.getOrderSn(), OrderConstant.OrderStatusEnum.PAYED.getCode());
        }
        return "success";
    }

    @Override
    public void handleSeckillOrder(SeckillOrderTo to) {
        OrderEntity order = new OrderEntity();
        order.setOrderSn(to.getOrderSn());
        order.setCreateTime(new Date());
        order.setMemberId(to.getMemberId());
        order.setSourceType(0);
        order.setStatus(OrderConstant.OrderStatusEnum.CREATE_NEW.getCode());
        order.setAutoConfirmDay(7);
        order.setBillType(0);
        order.setPayAmount(to.getSeckillPrice().multiply(new BigDecimal(to.getNum().toString())));
        order.setConfirmStatus(0);
        order.setDeleteStatus(0);
        order.setModifyTime(new Date());

        this.save(order);

        // 保存订单详细
        OrderItemEntity orderItem = new OrderItemEntity();
        orderItem.setSkuId(to.getSkuId());
        orderItem.setSkuQuantity(to.getNum().intValue());
        orderItem.setOrderSn(to.getOrderSn());
        orderItem.setSkuPrice(to.getSeckillPrice());
        orderItem.setRealAmount(to.getSeckillPrice().multiply(new BigDecimal(to.getNum().toString())));
        orderItemService.save(orderItem);
    }

    private void saveOrderInfo(OrderCreateTo to) {
        OrderEntity order = to.getOrder();
        this.save(order);
        List<OrderItemEntity> items = to.getItems();
        orderItemService.saveBatch(items);
    }

    /**
     * 生成订单信息
     *
     * @param vo 订单提交请求数据
     * @return 订单信息
     */
    private OrderCreateTo buildOrderInfo(OrderSubmitRequestVo vo, MemberResVo userInfo) {
        OrderCreateTo to = new OrderCreateTo();
        // 1. 基本订单信息
        // 生成订单 id
        String orderSn = IdWorker.getTimeId();
        OrderEntity order = buildBaseOrderInfo(vo, userInfo, to, orderSn);
        to.setOrder(order);

        // 2. 构建订单项
        List<OrderItemEntity> items = buildOrderItems(userInfo, orderSn);
        to.setItems(items);

        // 3. 计算价格
        computePrice(order, items);

        to.setPayPrice(order.getPayAmount());
        return to;
    }

    private void computePrice(OrderEntity order, List<OrderItemEntity> items) {
        BigDecimal couponAmount = BigDecimal.ZERO;
        BigDecimal promotionAmount = BigDecimal.ZERO;
        BigDecimal integrationAmount = BigDecimal.ZERO;
        BigDecimal realAmount = BigDecimal.ZERO;
        Integer giftIntegration = 0;
        Integer giftGrowth = 0;
        for (OrderItemEntity item : items) {
            couponAmount = couponAmount.add(item.getCouponAmount());
            promotionAmount = promotionAmount.add(item.getPromotionAmount());
            integrationAmount = integrationAmount.add(item.getIntegrationAmount());
            realAmount = realAmount.add(item.getRealAmount());

            giftIntegration += item.getGiftIntegration();
            giftGrowth = item.getGiftGrowth();
        }
        order.setCouponAmount(couponAmount);
        order.setPromotionAmount(promotionAmount);
        order.setIntegrationAmount(integrationAmount);
        order.setTotalAmount(realAmount);

        order.setPayAmount(realAmount.add(order.getFreightAmount()));

        order.setIntegration(giftIntegration);
        order.setGrowth(giftGrowth);
    }

    private OrderEntity buildBaseOrderInfo(OrderSubmitRequestVo vo, MemberResVo userInfo, OrderCreateTo to, String orderSn) {
        OrderEntity order = new OrderEntity();

        order.setOrderSn(orderSn);

        // 获取收获地址信息
        R r = wareFeignService.getFreight(vo.getAddressId());
        if (r.isOk()) {
            FreightRespTo data = r.getData(new TypeReference<FreightRespTo>() {
            });
            MemberAddressTo addressTo = data.getAddress();
            order.setCreateTime(new Date());
            order.setMemberId(addressTo.getMemberId());
            order.setMemberUsername(userInfo.getUsername());
            order.setPayType(1);
            order.setSourceType(0);
            order.setStatus(OrderConstant.OrderStatusEnum.CREATE_NEW.getCode());
            order.setAutoConfirmDay(7);
            order.setBillType(0);
            order.setReceiverName(addressTo.getName());
            order.setReceiverPhone(addressTo.getPhone());
            order.setReceiverPostCode(addressTo.getPostCode());
            order.setReceiverProvince(addressTo.getProvince());
            order.setReceiverCity(addressTo.getCity());
            order.setReceiverRegion(addressTo.getRegion());
            order.setReceiverDetailAddress(addressTo.getDetailAddress());
            order.setNote(vo.getOrderRemark());
            order.setConfirmStatus(0);
            order.setDeleteStatus(0);
            order.setModifyTime(new Date());

            // 设置运费信息

            order.setFreightAmount(data.getFreight());
            to.setFreight(data.getFreight());
        }
        return order;
    }

    private List<OrderItemEntity> buildOrderItems(MemberResVo userInfo, String orderSn) {
        List<OrderConfirmVo.OrderItemVo> vos = cartFeignService.cartItemsByMemberId(userInfo.getId());
        if (vos != null && !vos.isEmpty()) {
            return vos.stream().map(itemVo -> {
                OrderItemEntity item = buildOrderItem(itemVo);
                item.setOrderSn(orderSn);
                return item;
            }).collect(Collectors.toList());
        }
        return null;
    }

    private OrderItemEntity buildOrderItem(OrderConfirmVo.OrderItemVo itemVo) {
        OrderItemEntity item = new OrderItemEntity();
        Long skuId = itemVo.getSkuId();
        // 设置spu信息
        R r = productFeignService.getSpuInfoBySkuId(skuId);
        if (r.isOk()) {
            SpuInfoTo data = r.getData(new TypeReference<SpuInfoTo>() {
            });
            item.setSpuId(data.getId());
            item.setSpuName(data.getSpuName());
            item.setSpuPic(data.getSpuDescription());
            // 设置品牌属性
            item.setSpuBrand(data.getBrandId().toString());
            item.setCategoryId(data.getCatalogId());
        }

        // 设置sku信息
        item.setSkuId(itemVo.getSkuId());
        item.setSkuName(itemVo.getTitle());
        item.setSkuPic(itemVo.getImage());
        item.setSkuPrice(itemVo.getPrice());
        item.setSkuQuantity(itemVo.getCount());
        HashMap<String, String> attrMap = new HashMap<>();
        itemVo.getSkuAttrs().forEach(attr -> {
            String[] s = attr.split("：");
            attrMap.put(s[0], s[1]);
        });
        item.setSkuAttrsVals(JSON.toJSONString(attrMap));

        // todo 优惠券信息
        item.setPromotionAmount(BigDecimal.ZERO);
        item.setCouponAmount(BigDecimal.ZERO);
        item.setIntegrationAmount(BigDecimal.ZERO);
        item.setRealAmount(item.getSkuPrice().multiply(BigDecimal.valueOf(item.getSkuQuantity())));

        // 设置积分信息
        item.setGiftIntegration(itemVo.getPrice().intValue() * item.getSkuQuantity());
        item.setGiftGrowth(itemVo.getPrice().intValue() * item.getSkuQuantity());
        return item;
    }

}